/** 自定义的常用函数的类,属于静态类

  By setycyas @2023-09-27
*/

class MyFuns {

  /* 公有函数
  */
  ;从热键符号中获取真实热键.因为设定热键时使用的热键符号可能带有其他字符,目前只对应了前置的~字符.
  static getHotkey(hotkeyStr){
    local result := hotkeyStr
    if (SubStr(hotkeyStr, 1, 1) == "~")
	  result := SubStr(hotkeyStr, 2)
    return result
  }

  ;创建一个array,初值为startValue,步长为step,终值在step为正数时小于等于endValue,在step为负数时大于等于endValue
  ;注意在默认step下,初值终值都能取到,这与python的range不同,是autohotkey风格的range
  static range(startValue, endValue, step := 1){
    local v := startValue, result := []
    if (step == 0){
      return result
    }
    while (((step > 0) and (v <= endValue)) or ((step < 0) and (v >= endValue))) {
      result.push(v)
	  v += step
    }
    return result
  }

  ;把字符串数组strArr连起来,分隔符为seq
  static join(strArr, seq){
    local result := strArr[1], n := strArr.Length-1
    loop n {
      result := result . seq . strArr[A_Index+1]
    }
    return result
  }

  ;寻找一个数组的最大或最小值
  static arrMax(arr){
    local result := arr[1], n := arr.Length-1
    loop n {
      if (arr[A_Index+1] > result) {
	    result := arr[A_Index+1]
	  }
    }
    return result
  }
  static arrMin(arr){
    local result := arr[1], n := arr.Length-1
    loop n {
      if (arr[A_Index+1] < result) {
	    result := arr[A_Index+1]
	  }
    }
    return result
  }

  ;合并两个数组
  static arrCombine(arr1, arr2){
    local result := arr1.Clone()
    loop arr2.Length {
      result.push(arr2[A_Index])
    }
    return result
  }

  ;获取一个随机字符串,每一个字符可能是数字或小写字符,参数n为字符串长度
  static getRandomStr(n){
    local result := []
    local randNum
    loop n {
      randNum := Random(1, 36) ;10个数字加26个小写字母,一共有36种可能
      if (randNum < 11) {
        result.push(Chr(randNum+47))
      } else {
        result.push(Chr(randNum+86))
      }
    }
    return MyFuns.join(result, "")
  }

  ;** 与按键相关的函数
  ;按键,按下键,弹起键
  static press(key, duration := 25){
    send("{" . key . " down}")
    sleep(duration)
    send("{" . key . " up}")
  }
  static keyDown(key){
    send("{" . key . " down}")
  }
  static keyUp(key){
    send("{" . key . " up}")
  }

  ;连续按下多个按键keys[],每次间隔记录在delays[],最后间隔时间结束后全部放手.keys和delays长度要相等
  static pressKeys(keys, delays){

    local i, key, delay
    ;检测长度,不相等则退出
    if (keys.Length != delays.Length){
      return
    }
    ;按下按键
    for i, key in keys {
      delay := delays[i]
      send("{" . key . " down}")
      sleep(delay)
    }
    ;放开按键
    for i, key in keys {
      send("{" . key . " up}")
    }

  }

  ;技能输入,按键组合为keys,间隔为delays,相邻的两个按键有重合时间,也就是说按下下一个键才会放开上一个键
  ;keys和delays长度必须相等且最小为2
  static inputSkill(keys, delays){
    local i, curKey, curDelay
    ;检测长度
    if ((keys.Length != delays.Length) or (keys.Length < 2)){
      return
    }
    ;连续按键
    for i, curKey in keys {
      curDelay := delays[i]
      if (i < 3){
        send("{" . curKey . " down}")
        sleep(curDelay)
      } else {
        send("{" . keys[i-2] . " up}")
        send("{" . curKey . " down}")
        sleep(curDelay)
      }
    }
    ;放开最后的2个键
    send("{" . keys[keys.Length-1] . " up}")
    send("{" . keys[keys.Length] . " up}")
  }

  ;用DllCall自制的GetKeyState,底层效果比ahk v2的好
  ;但是!检测放开与否时,必须用~key的写法!!!否则会因为按下
  ;被屏蔽而无法检测!
  static getKeyState(key){
    local _key := MyFuns.getHotkey(key)
    if (DllCall("GetKeyState", "int", GetKeyVK(_key)) < 0) {
      return true
    } else {
      return false
    }
  }
  ;等待按键key放开,等待waitTime毫秒,每waitDuration毫秒检查一次,若发现按键放开返回true,等待完毕未放开返回false
  static waitKeyUp(key, waitTime, waitDuration := 20){
    local temp1 := 0 ;计算总等待时间
    local delta := 0 ;下一次等待时间
    ;local vk := GetKeyVK(MyFuns.getHotkey(key))
    ;local keyState := 0 ;储存DLL的GetKeyState的变量
    while (temp1 < waitTime) {
      delta := (waitTime-temp1 > waitDuration) ? (waitDuration) : (waitTime-temp1)
      ;if (!GetKeyState(key, "P")) {
      ;keyState := DllCall("GetKeyState", "int", vk)
      ;if (keyState > -1) { ;0或正数表示放开
      ;if (DllCall("GetKeyState", "int", vk) > -1) { ;0或正数表示放开
      ;if (!(DllCall("GetKeyState", "int", vk) < 0)) { ;0或正数表示放开
      if (!MyFuns.getKeyState(key)) {
        ;msgbox(keyState)
        return true
      }
      sleep(delta)
      temp1 := temp1+delta
    }
    return false
  }
  
  ;检查4个方向键,如按住则先松开
  static clearDirections(){
    static keys := ["left", "up", "right", "down"]
    static vkeys := [37, 38, 39, 40] ;方向键与方向键对应的虚拟键盘码
    loop 4 {
      if (DllCall("GetKeyState", "int", vkeys[A_Index]) < 0) {
        MyFuns.keyUp(keys[A_Index])
      }
    }
  }
}

/* 测试
*/

if (A_ScriptName == "MyFuns.2.ahk"){

  ;测试getHotkey(hotkeyStr)
  MsgBox(MyFuns.getHotkey("~space"))
  MsgBox(MyFuns.getHotkey("delete"))
  ;测试range,join,arrMax,arrMin
  arr1 := MyFuns.range(1, 10)
  Msgbox(MyFuns.join(arr1, ","))
  MsgBox(MyFuns.arrMax(arr1))
  MsgBox(MyFuns.arrMin(arr1))
  ;测试getRandomStr
  MsgBox(MyFuns.getRandomStr(10))

}
